/** @file    WidgetBuildingRenderer.cpp
 *  @author  Alessandro Polo
 *  @version $Id: WidgetBuildingRenderer.cpp 2891 2010-08-12 01:58:59Z alex $
 *  @brief
 * File containing methods for the wosh::gui::WidgetBuildingRenderer class.
 * The header for this class can be found in WidgetBuildingRenderer.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include "WidgetBuildingRenderer.h"
 #include "ui_WidgetBuildingRenderer.h"

 #include <QTimer>
 #include <qmath.h>


using namespace std;
namespace wosh {
 namespace gui {

// WOSH_REGISTER(wosh::gui::WidgetBuildingRenderer, "wosh::gui::IWidgetGeneric", 1.0, _static_WidgetBuildingRenderer )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WidgetBuildingRenderer::WidgetBuildingRenderer(QWidget *parent)
	: IWidgetGeneric(parent) {
	this->m_ui = new Ui::WidgetBuildingRenderer();
	this->m_ui->setupUi(this);

	this->m_ui->graphicsView->setRenderHint(QPainter::Antialiasing, false);
	this->m_ui->graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
	this->m_ui->graphicsView->setOptimizationFlags(QGraphicsView::DontSavePainterState);
	this->m_ui->graphicsView->setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
	this->m_ui->graphicsView->setAcceptDrops(true);

	if ( this->m_ui->graphicsView->scene() == NULL) {
		QGraphicsScene* scene = new QGraphicsScene();
		this->m_ui->graphicsView->setScene(scene );
	 }
	QTimer* timer = new QTimer(this);
	QObject::connect(timer, SIGNAL(timeout()), this->m_ui->graphicsView->scene(), SLOT(advance()));
	timer->start(1000 / 5);

	connect(this->m_ui->cmbHouse, SIGNAL(activated(const QString&)), this, SLOT(render_item(const QString&)));
	connect(this->m_ui->cmbFloor, SIGNAL(activated(const QString&)), this, SLOT(render_item(const QString&)));
	connect(this->m_ui->cmbRoom, SIGNAL(activated(const QString&)), this, SLOT(render_item(const QString&)));

	connect(this->m_ui->btnCanvasZoomIn, SIGNAL(clicked()), this, SLOT(gArea_zoomIn()));
	connect(this->m_ui->btnCanvasZoomOut, SIGNAL(clicked()), this, SLOT(gArea_zoomOut()));
    connect(this->m_ui->btnCanvasRotateLeft, SIGNAL(clicked()), this, SLOT(gArea_rotateLeft()));
    connect(this->m_ui->btnCanvasRotateRight, SIGNAL(clicked()), this, SLOT(gArea_rotateRight()));
	connect(this->m_ui->btnCanvasReset, SIGNAL(clicked()), this, SLOT(gArea_reset()));
	connect(this->m_ui->zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(gArea_setupMatrix()));
	connect(this->m_ui->rotateSlider, SIGNAL(valueChanged(int)), this, SLOT(gArea_setupMatrix()));

	connect(this->m_ui->btnReDraw, SIGNAL(clicked()), this, SLOT(reDrawAll()));

	// register to BUS
	BusBuilding.setMessageHandler(this);
	WRESULT bus_connected = BusBuilding.connect( _Bus_Building, Bus::ACTION_DEFERRED | Bus::BUS_CREATE_IF_MISSING );
	(void)bus_connected;

	QMenu* menuOptions = new QMenu(this);
	QMenu* menuItems = new QMenu(menuOptions);
	menuOptions->addAction( tr("Save Image..") );
	menuOptions->addMenu(menuItems);
	menuItems->setTitle( tr("Set Items") );
	this->actItemsMoveable = new QAction(tr("Moveable"), menuItems);
	this->actItemsMoveable->setCheckable(true);
	menuItems->addAction( this->actItemsMoveable );
	this->actItemsVisible = new QAction(tr("Visible"), menuItems);
	menuItems->addAction( this->actItemsVisible );
	this->m_ui->btnCanvasOptions->setMenu(menuOptions);
	connect(menuOptions, SIGNAL(triggered(QAction*)), this, SLOT(gArea_option_triggered(QAction*)));
 }

WidgetBuildingRenderer::~WidgetBuildingRenderer() {
	delete this->m_ui;
 }

void WidgetBuildingRenderer::changeEvent(QEvent *e) {
	QWidget::changeEvent(e);
	if ( e->type() == QEvent::LanguageChange )
		this->m_ui->retranslateUi(this);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetBuildingRenderer::refreshAll() {
	gArea_reset();
	this->m_ui->graphicsView->scene()->update();
 }

WRESULT WidgetBuildingRenderer::updateObject( const wosh::building::BuildingObject* bobj ) {
	if ( bobj == NULL ) {
		return WRET_ERR_PARAM;
	 }
	this->items.transactionBeginWrite();
	BuildingObjectGraphicsItem* bObjGraph = this->items.find(bobj->getName());
	BuildingObjectGraphicsItem* bObjParentGraph = this->items.find(bobj->getParentName());
	if ( bObjGraph == NULL ) {
		bObjGraph = createGraphicsItemFor(bobj);
		if ( bObjGraph == NULL ) {
			this->items.transactionEnd();
			return WRET_ERR_INTERNAL;
		 }
		bObjGraph->configureFor(bobj);
		bObjGraph->setParent(bObjParentGraph, true);
		this->items.set(bobj->getName(), bObjGraph);
		QGraphicsScene* scene = this->m_ui->graphicsView->scene();
		scene->addItem(bObjGraph);
	 }
	else {
		bObjGraph->configureFor(bobj);
	 }
	this->items.transactionEnd();
	return WRET_OK;
 }

WRESULT WidgetBuildingRenderer::renderObject( const std::string& bobj_name ) {
	this->items.transactionBeginRead();
	BuildingObjectGraphicsItem* bObjGraph = this->items.find(bobj_name);
	if ( bObjGraph == NULL ) {
		this->items.transactionEnd();
		return WRET_ERR_PARAM;
	 }
	this->items.transactionEnd();

	QGraphicsScene* scene = this->m_ui->graphicsView->scene();
	scene->update();

//reDrawAll();

	return WRET_OK;
 }

BuildingObjectGraphicsItem* WidgetBuildingRenderer::createGraphicsItemFor( const wosh::building::BuildingObject* bobj ) {
	if ( bobj == NULL ) return NULL;
	BuildingObjectGraphicsItem* bObjGraph = NULL;
	bObjGraph = BuildingObjectGraphicsItem::createItemOf( bobj->getInterfaces().getImplementedClass().getName(), this );
	return bObjGraph;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetBuildingRenderer::busMessage( const Message& message, const Bus* ) {
	if ( message.isEmpty() ) return;

	this->items.transactionBeginRead();
	BuildingObjectGraphicsItem* bObjGraph = this->items.find(message.getSource().getName());
	if ( bObjGraph == NULL ) {
		this->items.transactionEnd();
		return;
	 }
	bObjGraph->parseMessage(message);
	this->items.transactionEnd();
}

void WidgetBuildingRenderer::item_event( Message* message, BuildingObjectGraphicsItem* source ) {
	if ( message == NULL ) return;
	//message->setSource(this);
	message->setDestinationBroadcast(true);
	message->setDestinationBus(_Bus_Building);
	BusBuilding.postMessage(message);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetBuildingRenderer::render_item( const QString& text ) {
	this->items.transactionBeginRead();
	BuildingObjectGraphicsItem* bObjGraph = NULL;
	if ( text == "All Rooms" )
		bObjGraph = this->items.find( this->m_ui->cmbFloor->currentText().toLatin1().data() );
	else
		bObjGraph = this->items.find(text.toLatin1().data());

	if ( bObjGraph == NULL ) {
		this->items.transactionEnd();
		QMessageBox::warning(this, "Error", "Object not found" );
		return;
	 }
	QList<BuildingObjectGraphicsItem*> items = getItemsByType( bObjGraph->getRemoteType() );
	for(int i=0; i<items.size(); i++) {
		if ( items.at(i) == NULL ) continue;
		BuildingObjectGraphicsItem* cItem = items.at(i);
		if ( cItem == bObjGraph ) continue;
		cItem->setVisible(false);
		cItem->update();
	 }
	bObjGraph->setVisible(true);
	bObjGraph->update();
	this->items.transactionEnd();
	this->m_ui->graphicsView->scene()->update();
 }

void WidgetBuildingRenderer::refreshCombos() {
	this->m_ui->cmbHouse->clear();
	this->m_ui->cmbHouse->addItems( getItemsNameByType("wosh::building::House") );
	this->m_ui->cmbFloor->clear();
	this->m_ui->cmbFloor->addItems( getItemsNameByType("wosh::building::Floor") );
	this->m_ui->cmbRoom->clear();
	this->m_ui->cmbRoom->addItem("All Rooms");
	this->m_ui->cmbRoom->addItems( getItemsNameByType("wosh::building::Room") );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

QList<BuildingObjectGraphicsItem*> WidgetBuildingRenderer::getItemsByType( const string& itemType ) {
	QList<BuildingObjectGraphicsItem*> res_items;
	std::map<string,BuildingObjectGraphicsItem*>::const_iterator it;
	std::map<string,BuildingObjectGraphicsItem*>::const_iterator it_end = this->items.end();
	for ( it=this->items.begin(); it!=it_end; it++ ) {
		if ( it->second == NULL ) continue;
		if ( it->second->getRemoteType() == itemType )
		res_items.append(it->second);
	 }
	return res_items;
 }

QStringList WidgetBuildingRenderer::getItemsNameByType( const std::string& itemType ) {
	QStringList res_items;
	std::map<string,BuildingObjectGraphicsItem*>::const_iterator it;
	std::map<string,BuildingObjectGraphicsItem*>::const_iterator it_end = this->items.end();
	for ( it=this->items.begin(); it!=it_end; it++ ) {
		if ( it->second == NULL ) continue;
		if ( it->second->getRemoteType() == itemType )
		res_items.append(it->first.c_str());
	 }
	return res_items;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WidgetBuildingRenderer::gArea_setupMatrix() {
	qreal scale = qPow(qreal(2), (this->m_ui->zoomSlider->value() - 250) / qreal(50));
	QMatrix matrix;
	matrix.scale(scale, scale);
	matrix.rotate(this->m_ui->rotateSlider->value());
	this->m_ui->graphicsView->setMatrix(matrix);
 }

void WidgetBuildingRenderer::gArea_zoomIn() {
	this->m_ui->graphicsView->scale(1.2, 1.2);
 }
void WidgetBuildingRenderer::gArea_zoomOut() {
	this->m_ui->graphicsView->scale(1 / 1.2, 1 / 1.2);
 }
void WidgetBuildingRenderer::gArea_rotateLeft() {
	this->m_ui->rotateSlider->setValue(this->m_ui->rotateSlider->value() - 10);
 }
void WidgetBuildingRenderer::gArea_rotateRight() {
    this->m_ui->rotateSlider->setValue(this->m_ui->rotateSlider->value() + 10);
 }

void WidgetBuildingRenderer::gArea_reset() {
	this->m_ui->zoomSlider->setValue(250);
	this->m_ui->rotateSlider->setValue(0);
	gArea_setupMatrix();
	this->m_ui->graphicsView->ensureVisible(QRectF(0, 0, 0, 0));
	this->m_ui->graphicsView->scene()->update();
 }

void WidgetBuildingRenderer::gArea_save() {
	QPixmap pixmap;
	QPainter painter(&pixmap);
	painter.setRenderHint(QPainter::Antialiasing);
	this->m_ui->graphicsView->render(&painter);
	painter.end();
	pixmap.save("scene.png");
 }

void WidgetBuildingRenderer::gArea_option_triggered( QAction* action ) {
	if ( action == NULL ) return;
	if ( action->text() == tr("Save Image..") ) {
		gArea_save();
	 }
 	else if ( action == this->actItemsVisible ) {
		QGraphicsScene* scene = this->m_ui->graphicsView->scene();
		QList<QGraphicsItem*> its = scene->items();
		for(int i=0; i<its.size(); i++)
			its.at(i)->setVisible(true);
	 }
 	else if ( action == this->actItemsMoveable ) {
		QGraphicsScene* scene = this->m_ui->graphicsView->scene();
		QList<QGraphicsItem*> its = scene->items();
		for(int i=0; i<its.size(); i++)
			its.at(i)->setFlag(QGraphicsItem::ItemIsMovable, true);
//			its.at(0)->setFlag(QGraphicsItem::ItemIsMovable, action->isChecked());
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace gui
}; // namespace wosh
